<?php

class NCore_Utility_ConfigManager
{
	private static $configSettings;
	private static $configMode = 'default';
	
	public static function setMode($mode)
	{
		NCore_Utility_ConfigManager::$configMode = $mode;
	}

    public static function getMode()
    {
        return NCore_Utility_ConfigManager::$configMode;
    }
	
	public static function setUp($nephtali_config)
	{
		$default_settings['default']['error_handling']              = N::SUPPRESS_ERRORS;
		$default_settings['default']['error_page']                  = 'error.html';
		$default_settings['default']['profiling']                   = N::PROFILING_OFF;
		$default_settings['default']['debug']                       = N::DEBUG_OFF;
		$default_settings['default']['encryption_key']              = NULL;
		$default_settings['default']['encryption_salt']             = NULL;
		$default_settings['default']['upload_directory']            = '';
		$default_settings['default']['upload_ftp_url']              = NULL;
		$default_settings['default']['base_url_directory']          = '';
		$default_settings['default']['default_timezone']            = NULL;
		$default_settings['default']['sql_action_dsn']              = NULL;
		$default_settings['default']['sql_action_username']         = NULL;
		$default_settings['default']['sql_action_password']         = NULL;
		$default_settings['default']['sql_datasource_dsn']          = NULL;
		$default_settings['default']['sql_datasource_username']     = NULL;
		$default_settings['default']['sql_datasource_password']     = NULL;
		$default_settings['default']['protected_directories']       = array();
        $default_settings['default']['integrated_pages']            = array();
        $default_settings['default']['post_request_handling']       = N::AUTO_REDIRECT_POSTS_WO_FILES;
		
		if(array_key_exists(NCore_Utility_ConfigManager::$configMode, $nephtali_config))
		{
			NCore_Utility_ConfigManager::$configSettings = array_merge($default_settings['default'], $nephtali_config[NCore_Utility_ConfigManager::$configMode]);
		}
		else
		{
			throw new Exception('There was no configuration mode that matched the value \''.NCore_Utility_ConfigManager::$configMode.'\' set for this application.');
		}
	}

	public static function getSetting($setting)
	{
		if(array_key_exists($setting, NCore_Utility_ConfigManager::$configSettings))
		{
			return NCore_Utility_ConfigManager::$configSettings[$setting];
		}
	}
}

// Wrapper class that makes it easy to get important Nephtali stuff
class N
{
	protected static $encryptor = NULL;

	// Avoided use of hard-coded booleans to allow for future variations of options, as suggested by Kent Beck in Implementation Patterns

	// Filter options
	const ALLOW_INT = 1;
	const ALLOW_DOUBLE = 2;
	const ALLOW_ALPHANUM = 3;
	const ALLOW_EMAIL = 4;
	const ALLOW_URL = 5;
    const ALLOW_US_ZIP = 10;
    const ALLOW_US_PHONE = 11;
	const ALLOW_TEXT = 7;
    const ALLOW_MULTILINE_TEXT = 9;
	const ALLOW_XML = 6;
	const ALLOW_ALL = 8;
	
	// Regexes used elsewhere
	const REGEX_KEY = '/^[a-zA-Z]\w{0,49}$/';
	const REGEX_FILE_NAME = '/^[-a-zA-Z0-9_\s\.]+$/'; 
	const REGEX_URL = '/^(http|https):\/\/(([-a-zA-Z0-9]+)\.)*([-a-zA-Z0-9]+)\.([a-z]{2,6})(\/([-a-zA-Z0-9#_.=?+&;:%])+)*$/';
	// slight modification (removed ' and /) from last regex on:
	// http://www.regular-expressions.info/email.html
	const REGEX_EMAIL = '/^[a-z0-9!#$%&*+=?^_`{|}~-]+(?:\.[a-z0-9!#$%&*+=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+([a-z]{2,6})$/';
	
	// Whitelists (uses REGEX_URL from above)
	const WHITELIST_A_TAG = '/<a href="(http|https):\/\/(([-a-zA-Z0-9]+)\.)*([-a-zA-Z0-9]+)\.([a-z]{2,6})(\/([-a-zA-Z0-9#_.=?+&;:%])+)*">[ a-zA-Z0-9,)(\.\?!-]+<\/a>/';
	
	// ====================== CONFIG OPTIONS ========================
	// Error Handling
	const DISPLAY_ERRORS = 1;
	const SUPPRESS_AND_LOG_ERRORS = 2;
	const SUPPRESS_ERRORS = 3;
	
	// Profiling
	const APPEND_PROFILING_RESULTS = 1;
	const PROFILING_OFF = 2;
	
	// Debug
	const DEBUG_OFF = 1;
	const APPEND_DEBUG_INFO = 2;

    // Post Request Handling
    const AUTO_REDIRECT_POSTS_WO_FILES = 1;
    const AUTO_REDIRECT_ALL_POSTS = 2;
    const AUTO_REDIRECT_OFF = 3;
	
	// generator modes
	const MODE_STANDARD = 1;
	const MODE_HTMLFRAG = 2;
	const MODE_VALIDATE = 3;
	
	// handy method for getting config setting
	public static function config($setting)
	{
		return NCore_Utility_ConfigManager::getSetting($setting);
	}

    public static function getMode()
    {
        return NCore_Utility_ConfigManager::getMode();
    }
	
	public static function getEncryptor()
	{
		if(is_null(N::$encryptor))
			N::$encryptor = new NCore_Encryption();
			
		return N::$encryptor;
	}
	
	// wrapper to php's setcookie() method that encrypts the value
	public static function encryptedCookie()
	{
		$args = func_get_args();
		$enc = N::getEncryptor();
		$args[1] = $enc->encrypt($args[1]);
		return call_user_func_array('setcookie', $args);
	}
	
	// wrapper for writing to $_SESSION an encrypted value
	public static function encryptedSession($name, $value)
	{
		$enc = N::getEncryptor();
		return $_SESSION[$name] = $enc->encrypt($value);
	}
	
	public static function encrypt($value)
	{
		return N::getEncryptor()->encrypt($value);
	}
	
	public static function decrypt($value)
	{
		return N::getEncryptor()->decrypt($value);
	}
	
 
	/*
	 * Deprecated now that factory methods are in place
	 */
	public static function _($obj)
	{
		return $obj;
	}

	// used for escaping output on simple string replacements
	public static function safe_str_replace($search, $replace, $subject, $whitelist = NULL)
	{
		return str_replace($search, N::escape($replace, $whitelist), $subject);
	}
	// Method for outputing escaped strings
	public static function escape($string, $allowedHTML)
	{
		// use htmlspecialchars because it only does the 5 most important chars, and doesn't mess them up
		$string = htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
		
		if(!is_null($allowedHTML)) {
			foreach($allowedHTML as $tag) {
				// Save entitied version of tag, then look for occurances of it in string and replace them
				$entitied_tag = htmlspecialchars($tag, ENT_QUOTES, 'UTF-8');
				$string = str_replace($entitied_tag, $tag, $string);
			}
		}
		
		return $string;
	}
}

require_once('nconfig.php');

// prep config manager array
NCore_Utility_ConfigManager::setUp($nephtali_config);

// removes base_url_directory, which can pop up when sites are using temporary domains when DNS hasn't propagated.
function nephtali_remove_base_url_directory($path)
{
	return str_replace(N::config('base_url_directory'), '', $path);	
}

// takes into account include path when testing if file exists
function nephtali_file_exists($file)
{
	$exists = FALSE;

	$inc_paths = explode(PATH_SEPARATOR, get_include_path());

	foreach($inc_paths as $path) 
	{
		$path = $path.'/'.$file;
 
		if(file_exists($path))
		{
			$exists = TRUE;
			break;
		}
	}
 
	return $exists;
}

// Get Page class specific to current web page, if present.  Code found in Pages directory under matching hierarchy in public directory
if(nephtali_file_exists('NSite'.nephtali_remove_base_url_directory($_SERVER['PHP_SELF'])) || nephtali_remove_base_url_directory($_SERVER['PHP_SELF']) == '/nephtali_tests.php')
{
	// Class used to implement very primitive profiling within the framework
	class NCore_Profiler
	{
		private static $markers = array();
		private static $start;
		
		public static function start()
		{
			NCore_Profiler::$start = microtime(true);
		}
		
		public static function addMarker($label)
		{
			NCore_Profiler::$markers[$label] = microtime(true) - NCore_Profiler::$start;
		}
		
		public static function getMarkers()
		{
			return NCore_Profiler::$markers;
		}
	}
	
	// used for profiling
	NCore_Profiler::start();
	
	// Function for auto-loading classes in the Classes directory following the PEAR-style class naming convention
	function __autoload($class_name)
	{	
		$class_file = str_replace('_', '/', $class_name).'.php'; 
		require_once($class_file);
	}
	
	// Class that contains general functions
	class NF
	{
		public static function setDebugMode($debug)
		{
			// E_STRICT will be part of PHP 6, so might as well start now
			error_reporting(E_ALL | E_STRICT);
		
			// Make things exceptions instead of errors (when possible)
			set_error_handler('NF::errorHandler');
		
			if($debug == N::DISPLAY_ERRORS) {
				ini_set('display_errors', '1');
				set_exception_handler(array('NF','debugExceptionHandler'));
			} else {
				ini_set('display_errors', '0');
				set_exception_handler(array('NF','productionExceptionHandler')); 
			}
		}
		
		// Error handler that passes errors through to exception handler
		public static function errorHandler($errno, $errstr, $errfile, $errline)
		{
			// must take into account error suppressor (@) and not do anything with them (they equal 0)
			// http://framework.zend.com/issues/browse/ZF-3829
			if($errno < 1)
			{
				$error_msg = "<dl><dt>Error Message:</dt><dd>$errstr</dd><dt>File:</dt><dd>$errfile</dd><dt>Line:</dt><dd>$errline</dd></dl>"; 
				throw new Exception($error_msg);
			}
		}
	
		// Production exception handler
		public static function productionExceptionHandler($exception)
		{	
			// Get rid of any output that's already buffered
			ob_end_clean();
				
			// Send visitor to error page
			header('Location: '.NCore_Utility_ConfigManager::getSetting('error_page'));
		}
	
		// Debug exception handler
		public static function debugExceptionHandler($exception)
		{	
			// Get rid of any output that's already buffered
			ob_end_clean();
			
			?>
			<html>
				<head>
					<title>Debug output for unhandled exception</title>
				</head>
				<body>
					<h1>Debug output for unhandled exception</h1>
					<?php echo $exception->getMessage(); ?><br />
 					<h2>Debug Backtrace</h2> 
          <pre><?php print_r(debug_backtrace()); ?></pre> 
				</body>
			</html>
			<?php		
			die();
		}
			
		public static function filter($value)
		{
			// Define PHP hackemes used in filter
			$n_php_hackemes = array('<?', 'language=\'php', 'language="php', '<%');
	
			if (is_array($value)) {
			   return array_map(array('NF', 'filter'), $value);
			} else {
				if(get_magic_quotes_gpc()) {
					$value = trim(stripslashes($value));
				} else {
					$value = trim($value);
				}
				
				foreach($n_php_hackemes as $ph) {
					if(strpos(strtolower($value), $ph)===false) {
						continue;
					} else {
						throw new Exception('Some of the values we received were unsafe for execution.'); 
					}
				}
				
				return $value;
			}  
		}
		
		public static function filterPorts()
		{
			if(isset($_GET)) $portArrays[] = &$_GET;
			if(isset($_POST)) $portArrays[] = &$_POST;
			if(isset($_COOKIE)) $portArrays[] = &$_COOKIE;
	
			foreach($portArrays as &$arr)
			{
				$keys = array_keys($arr);
				foreach($keys as $key)
				{
					$arr[$key] = NF::filter($arr[$key]);
				}
			}			
		}
		
		public static function setDefaultTimezone($timezone)
		{
			if(!is_null($timezone))
			{
				if(!date_default_timezone_set($timezone))
				{
					throw new Exception('The default timezone could not be set.  Please check your config file setting \'default_timezone\' and make sure it matches one of the zones found at the page: http://us2.php.net/manual/en/timezones.php');
				}
			}
		}
	}

	// start output buffering right away because there is a Nephtali Page class that's going to be processed
	ob_start();

	// start the needed operations
	NF::setDebugMode(NCore_Utility_ConfigManager::getSetting('error_handling'));
	NF::filterPorts();
	NF::setDefaultTimezone(NCore_Utility_ConfigManager::getSetting('default_timezone'));
	
	// get the page
	include_once('NSite'.nephtali_remove_base_url_directory($_SERVER['PHP_SELF']));

    if(N::getMode() != 'test')
    {
        $n = new Page();
    }
    else
    {
        if(nephtali_file_exists('NTest'.nephtali_remove_base_url_directory($_SERVER['PHP_SELF'])))
        {
            include_once('NTest'.nephtali_remove_base_url_directory($_SERVER['PHP_SELF']));
            $n = new Test();
        }
        else
        {
            if(nephtali_remove_base_url_directory($_SERVER['PHP_SELF']) == '/nephtali_tests.php')
            {
                $n = new NCore_SiteTest();
            }
            else
            {
                $n = new NCore_PageTest();
            }
        }
    }
}   

?>